#include "Material.h"
#include "../framework/DeclManager.h"
#include <GLES/gl.h>

static int LayerToIndex( textureLayer_t layer ) {
    return layer;
}


Material::Material() : name("$$$")
{
    textureLayer.ready_num( 8 );
    inf::memory_set( &textureLayer[0], 0, textureLayer.maxSize()*sizeof(Texture*) );
}

Material::~Material()
{

}

const char* Material::GetName() const
{
    return name;
}

int Material::NumLayer() const
{
    return textureLayer.size();
}

Texture* Material::GetTexture( int layerIndex ) const
{
    return textureLayer[layerIndex];
}

Texture* Material::GetTextureLayer( textureLayer_t layer )
{
    return GetTexture( LayerToIndex(layer) );
}

void Material::BindTextureLayer( textureLayer_t layer, Texture* texture ) {
    textureLayer[ LayerToIndex(layer) ] = texture;
}

void Material::Apply()
{
    Texture*    texture = NULL;

    glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambientColor.to_float_ptr() );
    glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, diffuseColor.to_float_ptr() );
    glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specularColor.to_float_ptr() );

    glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, shinness );

    for( int i=0; i<textureLayer.size(); ++i ) {
        texture = textureLayer[i];
        if( texture ) {
            texture->Bind();
        }
    }
}

void Material::InitFromDecl( const Decl* decl ) {
    const char* diffuseTextureName = NULL;
    const char* diffuseTextureFile = NULL;

    Texture*    tex = NULL;

    name = decl->GetName();

    decl->GetColor4("ambient", ambientColor);
    decl->GetColor4("diffuse", ambientColor);
    decl->GetColor4("specular", ambientColor);
    decl->GetFloat("shinness", shinness);

    decl->GetString("diffuseTextureName", diffuseTextureName);
    decl->GetString("diffuseTextureFile", diffuseTextureFile);

    tex = textureManager->CreateFromFile( diffuseTextureFile );
    BindTextureLayer( TL_DIFFUSE, tex );
}

MaterialManager materialManagerLocal;
extern MaterialManager* materialManager = &materialManagerLocal;

MaterialManager::MaterialManager() {

}

MaterialManager::~MaterialManager() {

}

void MaterialManager::Init()
{
    defaultMaterial = errorMaterial = NULL;
}

void MaterialManager::Shutdown()
{
    inf::List<MaterialPtr>* materialList = (inf::List<MaterialPtr>*)&materials.get_value_list();
    inf::delete_contents( *materialList, false );

    materials.clear();

    defaultMaterial = errorMaterial = NULL;
}

void MaterialManager::ImportMaterialFromDeclManager()
{
    int iType;
    const char* typeName = NULL;

    const int numTypes = declManager->TypeNum();
    for( iType = 0; iType<numTypes; ++iType ) {
        typeName = declManager->GetTypeName( iType );
        if( inf::Str::cmp(typeName, "Material")==0 ) {
            break;
        }
    }

    if( iType == numTypes ) {
        common->Error("Checking failed to Material Decl Object in MaterialManager\n");
        return;
    }

    const DeclSeq& decls = declManager->GetDeclSeq( iType );
    const Decl* decl = NULL;
    MaterialPtr materialPtr = NULL;
    for( int i=0; i<decls.size(); ++i ) {
        decl = decls[i];

        materialPtr = new Material;
        materialPtr->InitFromDecl( decl );
        AddMaterial( materialPtr );
    }

    // setup default & error materials
    defaultMaterial = FindMaterial( "defaultMaterial" );
    errorMaterial = FindMaterial( "errorMaterial" );
}

int MaterialManager::NumMaterials() const
{
    return materials.size();
}

Material* MaterialManager::FindMaterial( const char* mtlName, bool makeDefault /*= true */ )
{
    const MaterialPtr* pMtl = NULL;
    inf::Str    lastToken(mtlName);
    lastToken.replace("\\", "/");
    int pos = lastToken.last('/');
    if( pos < lastToken.length() ) {
        lastToken = lastToken.right( lastToken.length()-pos-1 );
    }

    inf::Str    name;
    lastToken.extract_file_name( name );

    materials.get( name, &pMtl );
    if( !pMtl ) {
        common->Warning( "Failed to find material! name -- %s\n", mtlName );
        return NULL;
    }

    return *pMtl;
}

Material* MaterialManager::MaterialByIndex( int i ) const
{
    const MaterialPtr* pMtl = materials.get_index( i );
    if( !pMtl ) {
        common->Error( "Not exist the material index: %d\n", i );
        return NULL;
    }

    return *pMtl;
}

Material* MaterialManager::GetDefaultMaterial()
{
    return defaultMaterial;
}

Material* MaterialManager::GetErrorMaterial()
{
    return errorMaterial;
}

void MaterialManager::AddMaterial( Material* mtl )
{
    materials.set( mtl->GetName(), mtl );
}
